/*
 *  Creature.cpp
 *  sfmltest1
 *
 *  Created by Steven Hamilton on 25/06/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.Creature
 *
 */

#include <iostream>
#include <string>
#include <vector>
#include "Creature.h"
#include "DisplayManager.h"
#include "fov.h"
#include "GameWorld.h"
#include "Logger.h"
#include "Utility.h"
#include "Item.h"

class GameWorld;

Creature::Creature()
{
	totalHitPoints = PLAYER_TOTAL_HITPOINTS;
	currentHitPoints = totalHitPoints;
	totalActionPoints = PLAYER_TOTAL_ACTIONPOINTS;
	currentActionPoints = totalActionPoints;
	isAlive = true;
	faction = FRIEND;
	gfxId=PLAYER_S;
	fovMap.resize(MAPHEIGHT);
    endTurn = false;
	for (int i=0; i < MAPHEIGHT; ++i)
		fovMap[i].resize(MAPWIDTH);
	//redraw=true;
}

Creature::~Creature()
{
}

Creature::Creature(creature_t creatureType, faction_t fact)
{
    Utility tool;
    objectId=WORLD.getObjectId();
	type=creatureType;
    onMap=false;
	fovMap.resize(MAPHEIGHT);
	
    for (int i=0; i < MAPHEIGHT; ++i)
		fovMap[i].resize(MAPWIDTH);
	switch (type) {
        case HUMAN:{
			name = "Bob";
			xp = 0;
			xpValue=100;
			strength=tool.rollDice(1,10)+5;
			dexterity=tool.rollDice(1,10)+5;
			intelligence=tool.rollDice(1,10)+5;
			totalHitPoints = PLAYER_TOTAL_HITPOINTS;
			currentHitPoints = totalHitPoints;
			totalActionPoints = PLAYER_TOTAL_ACTIONPOINTS;
			currentActionPoints = totalActionPoints;
			isAlive = true;
			faction = fact;
			gfxId=PLAYER_S;
            wielding=0;
		}
			break;
		case SLITHER:{
			name="Slither";
			xpValue=20;
			totalHitPoints=10;
			currentHitPoints=totalHitPoints;
			totalActionPoints=20;
			currentActionPoints=totalActionPoints;
			isAlive=true;
			faction=fact;
			gfxId=SLITHER_S;
			position.x=(rand() % (MAPWIDTH-10))+10;
			position.y=rand() % MAPHEIGHT;
		
			//init AI
			aiFSM.initMachine(AI_MACH_SLITHER);
			
            //create a weapon for it
			Item* weapon=WORLD.generateItem(PISTOL, objectId);
            weapon->onMap=true;
            weapon->ownerObjectId=objectId;
			wielding = weapon->objectId;
            skills["Pistol"] = 10;
		}
			break;
		case GIMP:{
			name = "Gimp";
			xpValue=40;
			totalHitPoints = 20;
			currentHitPoints = totalHitPoints;
			totalActionPoints = 20;
			currentActionPoints = totalActionPoints;
			isAlive = true;
			faction = fact;
			gfxId=GIMP_S;
			position.x = (rand() % (MAPWIDTH-10))+10;
			position.y = rand() % MAPHEIGHT;
			
			//init AI
			aiFSM.initMachine(AI_MACH_GIMP);
			
            //create a weapon for it
			Item* weapon=WORLD.generateItem(PISTOL, objectId);
            weapon->onMap=true;
			wielding = weapon->objectId;
            skills["Pistol"] = 8;
		}
			
			break;
		default:
			break;
	}
	LOG(objectId,"Creating creature");
}

int Creature::capacity()
{
    return strength*STR_ENC_MODIFIER;
}

bool Creature::addToInventory(Item * item)
{
	//we check weight of object and current encumberance.
	std::vector<Item*> items=WORLD.getItems(objectId);
	std::vector<Item*>::iterator it;
	int encumberance=0;
	for (it=items.begin();it<items.end();it++){
		Item * item2=*it;
		encumberance+=item2->weight;
	}
	std::cout << "enc=" << capacity()-encumberance << std::endl;
	LOG("enc=" + capacity()-encumberance);
	if (item->weight<=capacity()-encumberance){
		item->ownerObjectId=objectId;
		return true;
	}
	return false;
}

std::string Creature::encumberanceString()
{
	Utility tool;
	std::vector<Item*> items=WORLD.getItems(objectId);
	std::vector<Item*>::iterator it;
	int encumberance=0;
	for (it=items.begin();it<items.end();it++){
		Item * item=*it;
		encumberance+=item->weight;
	}
	std::string str=tool.stringFromInt(encumberance)+"/"+tool.stringFromInt(capacity());
	return str;
}


bool Creature::move(move_t direction)
{	
	bool moved=false;
	//validate the position is on map
    Utility tool;
    Position newPosition=tool.getPositionFromDirection(position, direction);
    if (newPosition.validatePosition()){
		//ask the world if it's occupied
		if (!WORLD.isTileOccupied(newPosition)){
			int moveCost=WORLD.map.getMoveCost(newPosition);
			bool canMove = hasActionPoints(moveCost);
						
			//first ask the map if the tile is passable and we can afford to move
			if(WORLD.map.isTerrainPassable(newPosition) && canMove)
			 {
				//We only cost AP on a successful move
				currentActionPoints -= moveCost;
				
				//clear the tile we're leaving
				WORLD.map.setTilePassable(position, true);
				
				//move creature onto new tile
				position=newPosition;
				//std::cout << position.string() << std::endl;
				
				//make it unpassable
				WORLD.map.setTilePassable(position, false);
				moved=true;
			 }
		}
	}
	return moved;
}

void Creature::damage(int damage)
{
	//currently armourClass removes an absolute amount of damage. This is rubbish.
//	damage-=armour.armourClass;
	currentHitPoints-=damage;
	if (currentHitPoints<=0){
		currentHitPoints=0;
		setDead();
	}
}

void Creature::setDead()
{
	isAlive=false;
	onMap=false;
	MSG(name+" killed");
}

bool Creature::hasActionPoints(int cost)
{
	if (currentActionPoints >= cost) {
		return true;
	} else {
		MSG("Not enough action points");
		return false;
	}
}

bool Creature::isWielding()
{
    bool result = false;
    if (wielding) result = true;
    return result;
}

void Creature::resetAP()
{
	currentActionPoints = totalActionPoints;
}

void Creature::resetHP()
{
	currentHitPoints = totalHitPoints;
}

void Creature::processFOV()
{
	//updates creatures awareness
    Utility tool;
	Fov::prepareTransparencyMap();
	Fov::calculate (position.x, position.y, WORLD.map.parameterMap["visibility"]);
	//copy visible tiles into creatures fovMap
	for (int i=0;i < MAPWIDTH;i++){
		for (int j=0;j < MAPHEIGHT;j++){
			fovMap[j][i] = Fov::fovMap[i][j];
			//std::cout << fovMap[j][i];
		}
		//std::cout << std::endl;
	}
	
	//scan map for enemies and save their positions
	visibleEnemies.resize(0);
	//iterate the worlds onMap creatures and find the one's that matches a visible tile position and are enemies
    //get a list of all onMap creatures
    std::vector<Creature*> creatureList=WORLD.getCreatures(true);
	for (int p = 0;p < creatureList.size();p++){
		if (fovMap[creatureList[p]->position.y][creatureList[p]->position.x] && creatureList[p]->faction!=faction && creatureList[p]->isAlive) {
			visibleEnemies.push_back(creatureList[p]->position);
			std::cout << "id:" << objectId << " " << name << " is adding enemy id:" << creatureList[p]->objectId << " " << creatureList[p]->name <<" "<< creatureList[p]->position.string() << std::endl;
		}
	}
	//std::cout << "visibleEnemies.size=" << tool.stringFromInt(visibleEnemies.size()) << std::endl;
}


//--------------------------------
//---------AI CODE BELOW HERE-----
//--------------------------------

void Creature::aiFindTarget()
{
	Utility tool;
	double distance = 9999;
	
	//iterate through visibleEnemies and select the nearest valid one as target
	for (int i=0;i < visibleEnemies.size();i++){
		std::cout << "visibleEnemies.size=" << visibleEnemies.size() << std::endl;
		double newDistance=tool.getDistance(position, visibleEnemies[i]);
		if (newDistance<distance) {
			WORLD.target=visibleEnemies[i];
			//setTarget();
			std::cout << name << " is targetting " << visibleEnemies[i].x << ":" << visibleEnemies[i].y << " at distance " << newDistance << std::endl;
			return;
		}
	}
	return;
}

Position Creature::aiFindSafety()
{

	Utility tool;
	
	std::vector<Position> positions;
	positions.resize(0);
	int positionsIndex=0;
	Position goodPosition;
	bool foundSafety=false;
	
	//fetch a pathWeight map for our current position
	std::vector<std::vector<Position> > pathWeights=tool.pathWeight(position);
	
	//iterate map and select the one's within range
	for (int i=0; i<MAPHEIGHT;i++){
		for (int j=0; j<MAPWIDTH;j++){
			Position selectedPosition = pathWeights[i][j];
			if (selectedPosition.getPathWeight() <= currentActionPoints+1 && selectedPosition.getPathWeight()!=0 && selectedPosition.getPathWeight()!=1) positions.push_back(selectedPosition);
		}
	}
	
	//we then find the closest ones that we can't see our visible enemies from. To do this we must fetch a FOV map for each tile.
	
	//sort the list in order of pathweight so we start from the middle
	std::sort(positions.begin(), positions.end());
	
	//start iterating through all reachable positions.
	Position currentPosition;
jump:
	while (!foundSafety){
		currentPosition=positions[positionsIndex];
		//fetch FOV map from that position: NOTE! Remove monster positions from map before processing. Then they won't hide behind themselves.
		Fov::prepareTransparencyMap();
		//modify transparencyMap removing our monsters so they don't block vision
		std::vector<Creature*> creatureList = WORLD.getCreatures(ENEMY, true);

		for (int m=0;m<creatureList.size();m++){
			Fov::transparencyMap[creatureList[m]->position.x][creatureList[m]->position.y]=1;
		}
		//fetch FOV
		Fov::calculate (currentPosition.x, currentPosition.y, VISIBILITY_RANGE);
		
		//Iterate through all players;
        creatureList = WORLD.getCreatures(FRIEND, true);
		for (int p=0; p<creatureList.size();++p) {
			//if player's position is in the fovMap then it's visible. Jump out and try for next tile;
			if (Fov::fovMap[creatureList[p]->position.x][creatureList[p]->position.y]) {
				positionsIndex++;
				goto jump;
			}
		}
		goodPosition=currentPosition;
		foundSafety=true;
	}
	
	//print some debug
	//std::cout << "Found the following positions" << std::endl;
	//for (int i=0;i<20;i++){
	//	std::cout << positions[i].string() << std::endl;
	//}
	return goodPosition;
}

Position Creature::aiFindPatrolPoint()
{
    //returns a valid patrol point.
    Position point;
	point.x = rand() % MAPWIDTH;
	point.y = rand() % MAPHEIGHT;
    return point;
}

void Creature::aiMoveTo(std::vector<move_t> moves)
{
	//iterate moves list, move one square then update display until end
	for (int i=moves.size()-1; i > -1; i--) {
		move(moves[i]);
		std::cout << moves[i];
		DISPLAY.drawScreen();
		sf::Sleep(0.2f);
	}
	std::cout << std::endl;
}
